<?php
/**
 * Created by PhpStorm.
 * User: jjj
 * Date: 2018/1/9
 * Time: 18:22
 */

namespace app\common\lib\wechat;
use think\Cache;
use think\cache\Driver;

/**
 * Class WechatApi
 * @package app\sales\lib\wechat
 *
 * @property string AppId
 * @property string AppSecret
 *
 * @method mixed makeSignature(array $data) static 获取jsapi签名
 */
final class WechatApi
{
    private function __construct()
    {
        require_once __DIR__.'/../wxpay/WxPay.Config.php';
        $this->AppId = \WxPayConfig::APPID;
        $this->AppSecret = \WxPayConfig::APPSECRET;
    }

    private static $instance;
    public static function getInstance(): WechatApi {
        if (empty(self::$instance)) {
            self::$instance = new static();
        }
        return self::$instance;
    }

    private function cache() : Driver {
        return Cache::tag('wechat');
    }

    /**
     * 获取 access_token
     * @return mixed
     */
    private function getAccessToken() {

        $cache = $this->cache();

        $access_token = $cache->get('access_token');
        if ($access_token) return $access_token;

        $url = sprintf('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s', $this->AppId, $this->AppSecret);
        $resp = $this->request($url);

        $cache->set('access_token', $resp['access_token'], $resp['expires_in']);

        return $resp['access_token'];
    }

    /**
     * 获取 jsapi_ticket
     * @return mixed
     */
    private function getJsApiTicket() {

        $cache = $this->cache();

        $jsapi_ticket = $cache->get('jsapi_ticket');
        if ($jsapi_ticket) return $jsapi_ticket;

        $accessToken = $this->getAccessToken();
        $url = sprintf('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi', $accessToken);
        $resp = $this->request($url);

        $cache->set('jsapi_ticket', $resp['ticket'], $resp['expires_in']);

        return $resp['ticket'];
    }

    private function _makeSignature($data) {

        $data['jsapi_ticket'] = $this->getJsApiTicket();
        $data['noncestr'] = md5(mt_rand(111111,9999999).'');
        $data['timestamp'] = time();

        ksort($data);
        $signature = sha1($this->buildString($data));

        return [
            'appId' => $this->AppId,
            'timestamp' => $data['timestamp'],
            'nonceStr' => $data['noncestr'],
            'signature' => $signature,
        ];
    }

    private function buildString(array $data) {
        $i = 0;
        $str = '';
        foreach ($data as $k => $v) {
            if ($i > 0)
                $str .= "&$k=$v";
            else
                $str .= "$k=$v";
            $i++;
        }
        return $str;
    }

    /**
     *
     * @param $url
     * @param null $params
     * @param bool $isPost
     * @return mixed
     * @throws \Exception
     */
    private function request($url, $params = null, $isPost = false) {

        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,30);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);

        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);

        if ($isPost) {
            curl_setopt($ch,CURLOPT_POST, 1);
        }
        if (!empty($params)) {
            curl_setopt($ch,CURLOPT_POSTFIELDS, $params);
        }

        $resp = curl_exec($ch);
        if(curl_errno($ch)) {
            $err = 'curl error: '.curl_errno($ch).', '.curl_error($ch);
            curl_close($ch);
            throw new \Exception($err);
        }
        curl_close($ch);

        $resp = json_decode($resp, true);
        if (!empty($resp['errcode'])) {
            throw new \Exception($resp['errmsg'], $resp['errcode']);
        }

        return $resp;
    }

    public static function __callStatic($name, $arguments)
    {
        return call_user_func_array([self::getInstance(), '_'.$name], $arguments);
    }
}
